Attaching package: 㤼㸱lubridate㤼㸲
The following object is masked from 㤼㸱package:base㤼㸲:
date
Parsed with column specification:
cols(
.default = col_double(),
prosper_rating = [31mcol_character()[39m,
origination_date = [34mcol_datetime(format = "")[39m,
loan_status_description = [31mcol_character()[39m,
loan_default_reason = [33mcol_logical()[39m,
loan_default_reason_description = [33mcol_logical()[39m,
next_payment_due_date = [34mcol_datetime(format = "")[39m,
co_borrower_application = [33mcol_logical()[39m
)
See spec(...) for full column specifications.
266 parsing failures.
row col expected actual file
1957 loan_default_reason 1/0/T/F/TRUE/FALSE 2 'prosper2019.csv'
1957 loan_default_reason_description 1/0/T/F/TRUE/FALSE Bankruptcy 'prosper2019.csv'
3041 loan_default_reason 1/0/T/F/TRUE/FALSE 3 'prosper2019.csv'
3041 loan_default_reason_description 1/0/T/F/TRUE/FALSE Deceased 'prosper2019.csv'
3595 loan_default_reason 1/0/T/F/TRUE/FALSE 3 'prosper2019.csv'
.... ............................... .................. .......... .................
See problems(...) for more details.
pros_agg = pros_df%>%
mutate(yr = year(origination_date),mn = month(origination_date))%>%
#group_by(yr,mn, prosper_rating)%>%
group_by(origination_date,prosper_rating,yr,mn)%>%
summarize(avg_amt_borrowed = mean(amount_borrowed),
w_avg_rate = weighted.mean(borrower_rate,amount_borrowed))%>%
mutate(org_dt = ymd(paste(yr,mn,'01',sep = '-')))
pros_agg2 = pros_df%>%
mutate(yr = year(origination_date),mn = month(origination_date))%>%
group_by(yr,mn, prosper_rating)%>%
#group_by(origination_date,prosper_rating,yr,mn)%>%
summarize(avg_amt_borrowed = mean(amount_borrowed),
w_avg_rate = weighted.mean(borrower_rate,amount_borrowed))%>%
mutate(org_dt = ymd(paste(yr,mn,'01',sep = '-')))
A little about me before we begin:
Modeler at Bank of America
Avid Star Wars Fan

Amatuer user and ardent supporter of version control in data science

R user for the last 9 years.
Used R for modeling and development at American Credit Acceptance before coming to Bank of America.

- Introduced and championed Shiny for rapid application development
- Developed various models and process improvements using R tool kit
Bank of America work:

- Modeling Lead for Consumer Behavior Modeling:
- Worked on Risk Models for a year
- Currently working on Innovation team (Best job I think I will ever have):
- New modeling ideas
- New areas of the business that would benefit from modeling knowledge
- Application of deep learning
What is this presenation?
I enjoy the data visualization side of data science
ggplot was always go too for my data science needs
ggplot is an adoption of the precepts laid out in the Grammar of Graphics

ggplot doesn’t lend itself to interactivity
- several efforts have introduced interactivity
- ggvis
- rggobi
- iPlots
- htmlwidgets
- r2d3
htmlwidets and r2d3 are an adaptation of D3/javascript technology
D3 is a javascript package that is very important in data visualization
- Written in a language that integrates with web development
- Uses data to manipulate the web document through various objects (SVG)
- Released in 2011
Useful examples of power and interactivity:
Sunburst Example

D3 Pros and Cons
Pros - Very flexible and portable - Interactivity part of the dna - Looks very professional and polished
Cons - Very steep learning curve - API requires decent understanding of how javascript - Centered on webdevelopment instead of data science - ?Falling out of favor?
What is the answer?
Plot.ly is a solution with a simpler API and out of the box interactivity
Essentially plot.ly is API wrapper for several D3
How does the plotly package work
Key to understading package is understanding how it transforms the data
- Data enters in R formats
- Transformed to list format
- Tranformed to JSON format
Below is a useful diagram showing how the final presentation is done.

Plotly uses two key components:
- Data/Trace:
- Connection between data and visuals
- Traces have types (scatter plot, histograms, sunburst, etc.)
- Trace types have specific atttributes that can be defined.
- Layout
ggplotly() to the rescue
If you have plots in ggplot, you can start using plotly with just a simple function call on most ggplot objects.
Let’s make a ggplot from Propser data.
pros_scat = pros_df_sam%>%
ggplot(aes(principal_paid, interest_paid))+geom_point(aes(color = factor(prosper_rating)))
pros_scat

ggplotly(pros_scat)
NA
pros_df_sam%>%
plot_ly(x = ~principal_paid, y = ~interest_paid)%>%
add_markers(color = ~prosper_rating)
Do it again, but with lines:
amt_fin_p = pros_agg%>%
ggplot(aes(origination_date,avg_amt_borrowed))+
geom_line(aes(color = prosper_rating))
amt_fin_p

Simple ggplotly command adds the tooltip, zooming,
ggplotly(amt_fin_p)
Now the plotly syntax:
Same Graph different syntax (using the add_* trace addition)
pros_agg%>%
ungroup()%>%
plot_ly(x = ~origination_date, y = ~avg_amt_borrowed)%>%
add_lines(color = ~prosper_rating)
Histogram:
Histogram with muiltple factors
Now lets do this with 2 dimentions
subplt = subplot(
pros_df_sam%>%plot_ly(x = ~principal_paid, color = I("black"),type = 'histogram'),
plotly_empty(),
pros_df_sam%>%plot_ly(x = ~principal_paid,y = ~interest_paid,type = 'histogram2dcontour'),
pros_df_sam%>%plot_ly(y = ~interest_paid,color = I("black"),type = 'histogram'),
nrows = 2,
heights = c(0.2,0.8),
widths = c(0.8,0.2),
shareX = TRUE,
shareY = TRUE
)
No trace type specified and no positional attributes specifiedNo trace type specified:
Based on info supplied, a 'scatter' trace seems appropriate.
Read more about this trace type -> https://plot.ly/r/reference/#scatter
No scatter mode specifed:
Setting the mode to markers
Read more about this attribute -> https://plot.ly/r/reference/#scatter-mode
p = layout(subplt, showlegend = FALSE)
p
NA
x <- rnorm(1000)
y <- rnorm(1000)
s <- subplot(
plot_ly(x = x, color = I("black"), type = 'histogram'),
plotly_empty(),
plot_ly(x = x, y = y, type = 'histogram2dcontour', showscale = F),
plot_ly(y = y, color = I("black"), type = 'histogram'),
nrows = 2, heights = c(0.2, 0.8), widths = c(0.8, 0.2),
shareX = TRUE, shareY = TRUE, titleX = FALSE, titleY = FALSE
)
No trace type specified and no positional attributes specifiedNo trace type specified:
Based on info supplied, a 'scatter' trace seems appropriate.
Read more about this trace type -> https://plot.ly/r/reference/#scatter
No scatter mode specifed:
Setting the mode to markers
Read more about this attribute -> https://plot.ly/r/reference/#scatter-mode
p <- layout(s, showlegend = FALSE)
p
What does that look like in ggplot? Doable but not intuitive.
library(gridExtra)
hist_top <- pros_df_sam%>%ggplot(aes(principal_paid))+geom_histogram()
empty <- ggplot()+geom_point(aes(1,1), colour="white")+
theme(axis.ticks=element_blank(),
panel.background=element_blank(),
axis.text.x=element_blank(), axis.text.y=element_blank(),
axis.title.x=element_blank(), axis.title.y=element_blank())
scatter <- ggplot()+geom_density_2d(aes(pros_df_sam$principal_balance, pros_df_sam$interest_paid))
hist_right <- pros_df_sam%>%ggplot(aes(interest_paid))+geom_histogram()+coord_flip()
grid.arrange(hist_top, empty, scatter, hist_right, ncol=2, nrow=2, widths=c(4, 1), heights=c(1, 4))

Ack. Not as good.
Now some plots that are very difficult to do in ggplot and rely on the interactivity heavily.
Sunburst
LS0tDQp0aXRsZTogIlBsb3QubHk6IEhvdyBJIGxlYXJuZWQgYSAodmVyeSBsaXR0bGUpIGphdmEgc2NyaXB0IGFuZCBsb3ZlIGludGVyYWN0aXZlIHBsb3RzOiINCmF1dGhvcjogIkRhdmlkIEdyaW5kZXIiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpgYGB7ciwgZWNobz0gRkFMU0V9DQojSW1wb3J0IGxpYnJhcmllcw0KDQpsaWJyYXJ5KHBsb3RseSkNCmxpYnJhcnkobGlzdHZpZXdlcikNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpgYGANCg0KYGBge3IsIGVjaG8gPSBGQUxTRX0NCiNJbXBvcnRpbmcgZGF0YQ0KcHJvc19kZiA9IHJlYWRfY3N2KCJwcm9zcGVyMjAxOS5jc3YiKQ0KcHJvc19kZl9zYW0gPSBwcm9zX2RmW3NhbXBsZShucm93KHByb3NfZGYpLDEwMDAwKSxdDQpgYGANCg0KYGBge3J9DQoNCnByb3NfYWdnID0gcHJvc19kZiU+JQ0KICBtdXRhdGUoeXIgPSB5ZWFyKG9yaWdpbmF0aW9uX2RhdGUpLG1uID0gbW9udGgob3JpZ2luYXRpb25fZGF0ZSkpJT4lDQogICNncm91cF9ieSh5cixtbiwgcHJvc3Blcl9yYXRpbmcpJT4lDQogIGdyb3VwX2J5KG9yaWdpbmF0aW9uX2RhdGUscHJvc3Blcl9yYXRpbmcseXIsbW4pJT4lDQogIHN1bW1hcml6ZShhdmdfYW10X2JvcnJvd2VkID0gbWVhbihhbW91bnRfYm9ycm93ZWQpLA0KICAgICAgICAgICAgd19hdmdfcmF0ZSA9IHdlaWdodGVkLm1lYW4oYm9ycm93ZXJfcmF0ZSxhbW91bnRfYm9ycm93ZWQpKSU+JQ0KICBtdXRhdGUob3JnX2R0ID0geW1kKHBhc3RlKHlyLG1uLCcwMScsc2VwID0gJy0nKSkpDQogIA0KcHJvc19hZ2cyID0gcHJvc19kZiU+JQ0KICBtdXRhdGUoeXIgPSB5ZWFyKG9yaWdpbmF0aW9uX2RhdGUpLG1uID0gbW9udGgob3JpZ2luYXRpb25fZGF0ZSkpJT4lDQogIGdyb3VwX2J5KHlyLG1uLCBwcm9zcGVyX3JhdGluZyklPiUNCiAgI2dyb3VwX2J5KG9yaWdpbmF0aW9uX2RhdGUscHJvc3Blcl9yYXRpbmcseXIsbW4pJT4lDQogIHN1bW1hcml6ZShhdmdfYW10X2JvcnJvd2VkID0gbWVhbihhbW91bnRfYm9ycm93ZWQpLA0KICAgICAgICAgICAgd19hdmdfcmF0ZSA9IHdlaWdodGVkLm1lYW4oYm9ycm93ZXJfcmF0ZSxhbW91bnRfYm9ycm93ZWQpKSU+JQ0KICBtdXRhdGUob3JnX2R0ID0geW1kKHBhc3RlKHlyLG1uLCcwMScsc2VwID0gJy0nKSkpDQoNCg0KYGBgDQoNCg0KDQojIEEgbGl0dGxlIGFib3V0IG1lIGJlZm9yZSB3ZSBiZWdpbjoNCg0KDQpNb2RlbGVyIGF0IEJhbmsgb2YgQW1lcmljYQ0KDQpBdmlkIFN0YXIgV2FycyBGYW4NCg0KIVtdKHJldHVybl9vZl9qZWRpLmpwZykNCg0KQW1hdHVlciB1c2VyIGFuZCBhcmRlbnQgc3VwcG9ydGVyIG9mIHZlcnNpb24gY29udHJvbCBpbiBkYXRhIHNjaWVuY2UNCg0KIVtdKGdpdC5wbmcpDQoNClIgdXNlciBmb3IgdGhlIGxhc3QgOSB5ZWFycy4NCg0KVXNlZCBSIGZvciBtb2RlbGluZyBhbmQgZGV2ZWxvcG1lbnQgYXQgQW1lcmljYW4gQ3JlZGl0IEFjY2VwdGFuY2UgYmVmb3JlIGNvbWluZyB0byBCYW5rIG9mIEFtZXJpY2EuDQoNCiFbXShBQ0EucG5nKQ0KDQotIEludHJvZHVjZWQgYW5kIGNoYW1waW9uZWQgU2hpbnkgZm9yIHJhcGlkIGFwcGxpY2F0aW9uIGRldmVsb3BtZW50DQotIERldmVsb3BlZCB2YXJpb3VzIG1vZGVscyBhbmQgcHJvY2VzcyBpbXByb3ZlbWVudHMgdXNpbmcgUiB0b29sIGtpdA0KDQoqKkJhbmsgb2YgQW1lcmljYSB3b3JrOioqDQoNCiFbXShodHRwczovL3d3dy51bmRlcmNvbnNpZGVyYXRpb24uY29tL2JyYW5kbmV3L2FyY2hpdmVzL2Jhbmtfb2ZfYW1lcmljYV9sb2dvX2FuaW1hdGlvbl9uZXdfYS5naWYpDQoNCi0gTW9kZWxpbmcgTGVhZCBmb3IgQ29uc3VtZXIgQmVoYXZpb3IgTW9kZWxpbmc6DQotIFdvcmtlZCBvbiBSaXNrIE1vZGVscyBmb3IgYSB5ZWFyDQotIEN1cnJlbnRseSB3b3JraW5nIG9uIElubm92YXRpb24gdGVhbSAoQmVzdCBqb2IgSSB0aGluayBJIHdpbGwgZXZlciBoYXZlKToNCiAgLSBOZXcgbW9kZWxpbmcgaWRlYXMNCiAgLSBOZXcgYXJlYXMgb2YgdGhlIGJ1c2luZXNzIHRoYXQgd291bGQgYmVuZWZpdCBmcm9tIG1vZGVsaW5nIGtub3dsZWRnZQ0KICAtIEFwcGxpY2F0aW9uIG9mIGRlZXAgbGVhcm5pbmcNCg0KIyAqKldoYXQgaXMgdGhpcyBwcmVzZW5hdGlvbj8qKg0KDQojICoqSSBlbmpveSB0aGUgZGF0YSB2aXN1YWxpemF0aW9uIHNpZGUgb2YgZGF0YSBzY2llbmNlKioNCg0KIyMgKipnZ3Bsb3Qgd2FzIGFsd2F5cyBnbyB0b28gZm9yIG15IGRhdGEgc2NpZW5jZSBuZWVkcyoqDQoNCi0gc2ltcGxlIGludHVpdGl2ZSBzeW50YXggdG8gd3JpdGUgYW5kIHJlYWQNCi0gd2lkZSByYW5nZSBvZiB1c2VmdWwgZ2VvbXMgdG8gc29sdmUgY29tbW9uIG1vZGVsaW5nIHByb2JsZW1zDQotIGludGVncmF0ZXMgd2VsbCB3aXRoIHRoZSByZXN0IG9mIHRpZHl2ZXJzZQ0KDQotIChCaWcgc3RlcCBmb3J3YXJkIGZyb20gbGF0dGljZSBhbmQgYmFzZSBncmFwaGljcykNCg0KDQoNCiMjIyAqKmdncGxvdCBpcyBhbiBhZG9wdGlvbiBvZiB0aGUgcHJlY2VwdHMgbGFpZCBvdXQgaW4gdGhlIEdyYW1tYXIgb2YgR3JhcGhpY3MqKiANCg0KIVtdKEdHX2NvbmNlcHRzLnBuZykNCg0KIyMgZ2dwbG90IGRvZXNuJ3QgbGVuZCBpdHNlbGYgdG8gaW50ZXJhY3Rpdml0eQ0KDQotIHNldmVyYWwgZWZmb3J0cyBoYXZlIGludHJvZHVjZWQgaW50ZXJhY3Rpdml0eSANCiAgLSBnZ3Zpcw0KICAtIHJnZ29iaQ0KICAtIGlQbG90cw0KICAtIGh0bWx3aWRnZXRzDQogIC0gcjJkMw0KDQojIGh0bWx3aWRldHMgYW5kIHIyZDMgYXJlIGFuIGFkYXB0YXRpb24gb2YgRDMvamF2YXNjcmlwdCB0ZWNobm9sb2d5DQoNCiMjIEQzIGlzIGEgamF2YXNjcmlwdCBwYWNrYWdlIHRoYXQgaXMgdmVyeSBpbXBvcnRhbnQgaW4gZGF0YSB2aXN1YWxpemF0aW9uDQoNCi0gV3JpdHRlbiBpbiBhIGxhbmd1YWdlIHRoYXQgaW50ZWdyYXRlcyB3aXRoIHdlYiBkZXZlbG9wbWVudA0KLSBVc2VzIGRhdGEgdG8gbWFuaXB1bGF0ZSB0aGUgd2ViIGRvY3VtZW50IHRocm91Z2ggdmFyaW91cyBvYmplY3RzIChTVkcpDQotIFJlbGVhc2VkIGluIDIwMTENCg0KIyMjIFVzZWZ1bCBleGFtcGxlcyBvZiBwb3dlciBhbmQgaW50ZXJhY3Rpdml0eToNCg0KIyBTdW5idXJzdCBFeGFtcGxlDQoNCiFbXShodHRwczovL2kuc3RhY2suaW1ndXIuY29tL0g2TzJLLmdpZikNCg0KIyBEZW5zaXR5IG92ZXIgdGltZQ0KDQohW10oaHR0cHM6Ly9tZWRpYS5naXBoeS5jb20vbWVkaWEvTlRqaXVza0lNRTZhd0tuMW5EL2dpcGh5LmdpZikNCg0KW0RlY2lzaW9uIFRyZWUgRXhhbXBsZV0oaHR0cDovL2JsLm9ja3Mub3JnL2ZyYWN0YWx5dGljcy9yYXcvNDk1YjYzY2Y2NzFiNGM0ODdiYzQwODAxMzY2Mzg0ZTAvKQ0KDQoNCiMgKipEMyBQcm9zIGFuZCBDb25zKioNCg0KKlByb3MqDQotIFZlcnkgZmxleGlibGUgYW5kIHBvcnRhYmxlDQotIEludGVyYWN0aXZpdHkgcGFydCBvZiB0aGUgZG5hDQotIExvb2tzIHZlcnkgcHJvZmVzc2lvbmFsIGFuZCBwb2xpc2hlZA0KDQoqQ29ucyoNCi0gVmVyeSBzdGVlcCBsZWFybmluZyBjdXJ2ZQ0KICAtIEFQSSByZXF1aXJlcyBkZWNlbnQgdW5kZXJzdGFuZGluZyBvZiBob3cgamF2YXNjcmlwdA0KLSBDZW50ZXJlZCBvbiB3ZWJkZXZlbG9wbWVudCBpbnN0ZWFkIG9mIGRhdGEgc2NpZW5jZQ0KLSA/RmFsbGluZyBvdXQgb2YgZmF2b3I/DQoNCiMjIFdoYXQgaXMgdGhlIGFuc3dlcj8NCg0KIyAqKlBsb3QubHkgaXMgYSBzb2x1dGlvbiB3aXRoIGEgc2ltcGxlciBBUEkgYW5kIG91dCBvZiB0aGUgYm94IGludGVyYWN0aXZpdHkqKg0KDQpFc3NlbnRpYWxseSBwbG90Lmx5IGlzIEFQSSB3cmFwcGVyIGZvciBzZXZlcmFsIEQzDQoNCg0KIyAqKkhvdyBkb2VzIHRoZSBwbG90bHkgcGFja2FnZSB3b3JrKioNCg0KS2V5IHRvIHVuZGVyc3RhZGluZyBwYWNrYWdlIGlzIHVuZGVyc3RhbmRpbmcgaG93IGl0IHRyYW5zZm9ybXMgdGhlIGRhdGENCg0KLSBEYXRhIGVudGVycyBpbiBSIGZvcm1hdHMNCi0gVHJhbnNmb3JtZWQgdG8gbGlzdCBmb3JtYXQgDQotIFRyYW5mb3JtZWQgdG8gSlNPTiBmb3JtYXQNCg0KQmVsb3cgaXMgYSB1c2VmdWwgZGlhZ3JhbSBzaG93aW5nIGhvdyB0aGUgZmluYWwgcHJlc2VudGF0aW9uIGlzIGRvbmUuDQoNCiFbXShwbG90bHlfZGF0YV90cmFuc2Zvcm0uc3ZnKQ0KDQoqKlBsb3RseSB1c2VzIHR3byBrZXkgY29tcG9uZW50czoqKg0KDQoxLiBEYXRhL1RyYWNlOg0KICAtIENvbm5lY3Rpb24gYmV0d2VlbiBkYXRhIGFuZCB2aXN1YWxzDQogIC0gVHJhY2VzIGhhdmUgdHlwZXMgKHNjYXR0ZXIgcGxvdCwgaGlzdG9ncmFtcywgc3VuYnVyc3QsIGV0Yy4pDQogIC0gVHJhY2UgdHlwZXMgaGF2ZSBzcGVjaWZpYyBhdHR0cmlidXRlcyB0aGF0IGNhbiBiZSBkZWZpbmVkLg0KMi4gTGF5b3V0DQoNCg0KIyAqKmdncGxvdGx5KCkgdG8gdGhlIHJlc2N1ZSoqDQoNCklmIHlvdSBoYXZlIHBsb3RzIGluIGdncGxvdCwgeW91IGNhbiBzdGFydCB1c2luZyBwbG90bHkgd2l0aCBqdXN0IGEgc2ltcGxlIGZ1bmN0aW9uIGNhbGwgb24gbW9zdCBnZ3Bsb3Qgb2JqZWN0cy4NCg0KDQpMZXQncyBtYWtlIGEgZ2dwbG90IGZyb20gUHJvcHNlciBkYXRhLiAgDQpgYGB7cn0NCnByb3Nfc2NhdCA9IHByb3NfZGZfc2FtJT4lDQogIGdncGxvdChhZXMocHJpbmNpcGFsX3BhaWQsIGludGVyZXN0X3BhaWQpKStnZW9tX3BvaW50KGFlcyhjb2xvciA9IGZhY3Rvcihwcm9zcGVyX3JhdGluZykpKQ0KDQpwcm9zX3NjYXQNCmBgYA0KDQoNCg0KYGBge3J9DQpnZ3Bsb3RseShwcm9zX3NjYXQpDQoNCmBgYA0KDQpgYGB7cn0NCg0KDQpwcm9zX2RmX3NhbSU+JQ0KICBwbG90X2x5KHggPSB+cHJpbmNpcGFsX3BhaWQsIHkgPSB+aW50ZXJlc3RfcGFpZCklPiUNCiAgYWRkX21hcmtlcnMoY29sb3IgPSB+cHJvc3Blcl9yYXRpbmcpDQpgYGANCg0KDQoNCkRvIGl0IGFnYWluLCBidXQgd2l0aCBsaW5lczoNCmBgYHtyfQ0KYW10X2Zpbl9wID0gcHJvc19hZ2clPiUNCiAgZ2dwbG90KGFlcyhvcmlnaW5hdGlvbl9kYXRlLGF2Z19hbXRfYm9ycm93ZWQpKSsNCiAgZ2VvbV9saW5lKGFlcyhjb2xvciA9IHByb3NwZXJfcmF0aW5nKSkNCg0KYW10X2Zpbl9wDQpgYGANCg0KU2ltcGxlIGdncGxvdGx5IGNvbW1hbmQgYWRkcyB0aGUgdG9vbHRpcCwgem9vbWluZywgDQoNCmBgYHtyfQ0KZ2dwbG90bHkoYW10X2Zpbl9wKQ0KYGBgDQoNCk5vdyB0aGUgcGxvdGx5IHN5bnRheDoNCg0KYGBge3J9DQpwcm9zX2FnZyU+JQ0KICB1bmdyb3VwKCklPiUNCiAgcGxvdF9seSh4ID0gfm9yaWdpbmF0aW9uX2RhdGUsIHkgPSB+YXZnX2FtdF9ib3Jyb3dlZCwgY29sb3IgPSB+cHJvc3Blcl9yYXRpbmcsIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMnKQ0KIyAgYWRkX2xpbmVzKGNvbG9yID0gfnByb3NwZXJfcmF0aW5nKQ0KYGBgDQpTYW1lIEdyYXBoIGRpZmZlcmVudCBzeW50YXggKHVzaW5nIHRoZSBhZGRfKiB0cmFjZSBhZGRpdGlvbikNCmBgYHtyfQ0KcHJvc19hZ2clPiUNCiAgdW5ncm91cCgpJT4lDQogIHBsb3RfbHkoeCA9IH5vcmlnaW5hdGlvbl9kYXRlLCB5ID0gfmF2Z19hbXRfYm9ycm93ZWQpJT4lDQogIGFkZF9saW5lcyhjb2xvciA9IH5wcm9zcGVyX3JhdGluZykNCmBgYA0KDQpIaXN0b2dyYW06DQpgYGB7cn0NCnByb3NfZGZfc2FtJT4lDQogIHBsb3RfbHkoeCA9IH5ib3Jyb3dlcl9yYXRlKSU+JQ0KICBhZGRfaGlzdG9ncmFtKCkNCmBgYA0KDQpIaXN0b2dyYW0gd2l0aCBtdWlsdHBsZSBmYWN0b3JzDQpgYGB7cn0NCg0KcHJvc19kZl9zYW0lPiUNCiAgcGxvdF9seSh4ID0gfmZhY3Rvcih0ZXJtKSwgY29sb3IgPSB+cHJvc3Blcl9yYXRpbmcpJT4lDQogIGFkZF9oaXN0b2dyYW0oKQ0KDQpgYGANCg0KTm93IGxldHMgZG8gdGhpcyB3aXRoIDIgZGltZW50aW9ucw0KDQpgYGB7cn0NCg0KcHJvc19kZiU+JQ0KICBwbG90X2x5KA0KICAgIHggPSB+cHJpbmNpcGFsX3BhaWQsDQogICAgeSA9IH5pbnRlcmVzdF9wYWlkLA0KICAgIHR5cGUgPSAnaGlzdG9ncmFtMmRjb250b3VyJw0KICApDQogIA0KICANCiAgDQpgYGANCg0KYGBge3J9DQoNCnN1YnBsdCA9IHN1YnBsb3QoDQogIA0KICBwcm9zX2RmX3NhbSU+JXBsb3RfbHkoeCA9IH5wcmluY2lwYWxfcGFpZCwgY29sb3IgPSBJKCJibGFjayIpLHR5cGUgPSAnaGlzdG9ncmFtJyksDQogIHBsb3RseV9lbXB0eSgpLA0KICBwcm9zX2RmX3NhbSU+JXBsb3RfbHkoeCA9IH5wcmluY2lwYWxfcGFpZCx5ID0gfmludGVyZXN0X3BhaWQsdHlwZSA9ICdoaXN0b2dyYW0yZGNvbnRvdXInKSwNCiAgcHJvc19kZl9zYW0lPiVwbG90X2x5KHkgPSB+aW50ZXJlc3RfcGFpZCxjb2xvciA9IEkoImJsYWNrIiksdHlwZSA9ICdoaXN0b2dyYW0nKSwNCiAgbnJvd3MgPSAyLCAgDQogIGhlaWdodHMgPSBjKDAuMiwwLjgpLCANCiAgd2lkdGhzID0gYygwLjgsMC4yKSwNCiAgc2hhcmVYID0gVFJVRSwNCiAgc2hhcmVZID0gVFJVRQ0KKQ0KDQpwID0gbGF5b3V0KHN1YnBsdCwgc2hvd2xlZ2VuZCA9IEZBTFNFKQ0KICANCnANCiAgDQpgYGANCg0KDQoNCg0KYGBge3J9DQp4IDwtIHJub3JtKDEwMDApDQp5IDwtIHJub3JtKDEwMDApDQpzIDwtIHN1YnBsb3QoDQogIHBsb3RfbHkoeCA9IHgsIGNvbG9yID0gSSgiYmxhY2siKSwgdHlwZSA9ICdoaXN0b2dyYW0nKSwgDQogIHBsb3RseV9lbXB0eSgpLCANCiAgcGxvdF9seSh4ID0geCwgeSA9IHksIHR5cGUgPSAnaGlzdG9ncmFtMmRjb250b3VyJywgc2hvd3NjYWxlID0gRiksIA0KICBwbG90X2x5KHkgPSB5LCBjb2xvciA9IEkoImJsYWNrIiksIHR5cGUgPSAnaGlzdG9ncmFtJyksDQogIG5yb3dzID0gMiwgaGVpZ2h0cyA9IGMoMC4yLCAwLjgpLCB3aWR0aHMgPSBjKDAuOCwgMC4yKSwgDQogIHNoYXJlWCA9IFRSVUUsIHNoYXJlWSA9IFRSVUUsIHRpdGxlWCA9IEZBTFNFLCB0aXRsZVkgPSBGQUxTRQ0KKQ0KDQpwIDwtIGxheW91dChzLCBzaG93bGVnZW5kID0gRkFMU0UpDQoNCnANCmBgYA0KDQoNCldoYXQgZG9lcyB0aGF0IGxvb2sgbGlrZSBpbiBnZ3Bsb3Q/ICBEb2FibGUgYnV0IG5vdCBpbnR1aXRpdmUuDQpgYGB7cn0NCmxpYnJhcnkoZ3JpZEV4dHJhKQ0KDQpoaXN0X3RvcCA8LSBwcm9zX2RmX3NhbSU+JWdncGxvdChhZXMocHJpbmNpcGFsX3BhaWQpKStnZW9tX2hpc3RvZ3JhbSgpDQplbXB0eSA8LSBnZ3Bsb3QoKStnZW9tX3BvaW50KGFlcygxLDEpLCBjb2xvdXI9IndoaXRlIikrDQogICAgICAgICB0aGVtZShheGlzLnRpY2tzPWVsZW1lbnRfYmxhbmsoKSwgDQogICAgICAgICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kPWVsZW1lbnRfYmxhbmsoKSwgDQogICAgICAgICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCksIGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSwgICAgICAgICAgIA0KICAgICAgICAgICAgICAgYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aXRsZS55PWVsZW1lbnRfYmxhbmsoKSkNCg0Kc2NhdHRlciA8LSBnZ3Bsb3QoKStnZW9tX2RlbnNpdHlfMmQoYWVzKHByb3NfZGZfc2FtJHByaW5jaXBhbF9iYWxhbmNlLCBwcm9zX2RmX3NhbSRpbnRlcmVzdF9wYWlkKSkNCg0KaGlzdF9yaWdodCA8LSBwcm9zX2RmX3NhbSU+JWdncGxvdChhZXMoaW50ZXJlc3RfcGFpZCkpK2dlb21faGlzdG9ncmFtKCkrY29vcmRfZmxpcCgpDQoNCmdyaWQuYXJyYW5nZShoaXN0X3RvcCwgZW1wdHksIHNjYXR0ZXIsIGhpc3RfcmlnaHQsIG5jb2w9MiwgbnJvdz0yLCB3aWR0aHM9Yyg0LCAxKSwgaGVpZ2h0cz1jKDEsIDQpKQ0KYGBgDQoNCkFjay4gIE5vdCBhcyBnb29kLiAgDQoNCg0KTm93IHNvbWUgcGxvdHMgdGhhdCBhcmUgdmVyeSBkaWZmaWN1bHQgdG8gZG8gaW4gZ2dwbG90IGFuZCByZWx5IG9uIHRoZSBpbnRlcmFjdGl2aXR5IGhlYXZpbHkuDQoNClN1bmJ1cnN0DQoNCmBgYHtyfQ0KYGBgDQoNCg==